//
//  Btea.swift
//  AILock
//
//  Created by peiw on 2018/3/10.
//  Copyright © 2018年 imon. All rights reserved.
//

import Foundation

let XXTEA_DELTA : UInt32 = 0x9e3779b9

public func xxteaMX(z : UInt32, y : UInt32, sum : UInt32, p : Int, e : UInt32, key : [UInt32] ) -> UInt32 {
    //#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
    let p1 : UInt32 = (z>>5^y<<2)
    let p2 : UInt32 = (y>>3^z<<4)
    
    let s1 : UInt32 = (sum^y)
    let i : Int = (p&3) ^ Int(e)
    let k1 : UInt32 = (key[i] ^ z)
    
    let pp = p1 &+ p2
    let sk = s1 &+ k1
    
    return (pp ^ sk)
}

public func xxteaEncrypt(plainValue : [UInt32], k : [UInt32]) -> [UInt32] {
    let n : Int = plainValue.count
    var y : UInt32 = 0
    var z : UInt32
    var sum : UInt32 = 0
    let rounds : Int = 6 + (52/n)
    var e : UInt32 = 0
    var v : [UInt32] = plainValue
    let pValue = n - 1
    
    z = v[n-1]
    for _ in 0..<rounds {
        
        sum = sum &+ XXTEA_DELTA
        e = (sum >> 2) & 3;
        
        for p in 0..<n-1 {
            y = v[p+1]
            v[p] = v[p] &+ xxteaMX(z:z, y: y, sum: sum, p: p, e: e, key: k)
            z = v[p]
        }
        
        y = v[0]
        v[n-1] = v[n-1] &+ xxteaMX(z: z, y: y, sum: sum, p: pValue, e: e, key: k)
        z = v[n-1]
    }
    
    return v
}

public func xxteaDecrypt(encryptedValue : [UInt32], k : [UInt32]) -> [UInt32] {
    let n : Int = encryptedValue.count
    let rounds : Int = 6 + 52/n
    var sum : UInt32 = 0
    var v : [UInt32] = encryptedValue
    var y : UInt32 = 0
    var z : UInt32
    var e : UInt32 = 0
    let pValue = 0
    
    sum = UInt32(rounds) &* XXTEA_DELTA;
    y = v[0];
    for _ in 0 ..< rounds {
        e = (sum >> 2) & 3
        
        for p in (1...n-1).reversed() {
            z = v[p-1]
            v[p] =  v[p] &- xxteaMX(z: z, y: y, sum: sum, p: p, e: e, key: k)
            y = v[p]
        }
        
        z = v[n-1]
        v[0] = v[0] &- xxteaMX(z: z, y: y, sum: sum, p: pValue, e: e, key: k)
        y = v[0]
        sum = sum &- XXTEA_DELTA;
    }
    
    return v
}
